/*
 * Copyright 2022 CloudWeGo Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package mw

import (
	"context"
	"net/http"
	"time"
	"user_service/biz/dal/model"
	"user_service/biz/dal/query"
	"user_service/biz/handler"
	"user_service/biz/model/user"

	"github.com/cloudwego/hertz/pkg/app"
	"github.com/cloudwego/hertz/pkg/common/hlog"
	"github.com/cloudwego/hertz/pkg/common/utils"
	"github.com/cloudwego/hertz/pkg/protocol/consts"
	"github.com/hertz-contrib/jwt"
	"golang.org/x/crypto/bcrypt"
)

var JwtMiddleware *jwt.HertzJWTMiddleware

const (
	IdentityKey    = "identity"
	identityUserID = "identity_user_id"
	SecretKey      = "secret key"
)

func InitJwt() {
	var err error
	JwtMiddleware, err = jwt.New(&jwt.HertzJWTMiddleware{
		Realm:         "test zone",
		Key:           []byte(SecretKey),
		Timeout:       time.Hour,
		MaxRefresh:    time.Hour,
		TokenLookup:   "header: Authorization, query: token, cookie: jwt",
		TokenHeadName: "Bearer",
		LoginResponse: func(ctx context.Context, c *app.RequestContext, code int, token string, expire time.Time) {
			c.JSON(http.StatusOK, utils.H{
				"code":   code,
				"token":  token,
				"expire": expire.Format(time.RFC3339),
				"msg":    "success",
			})
		},
		LogoutResponse: func(ctx context.Context, c *app.RequestContext, code int) {
			c.JSON(http.StatusOK, utils.H{
				"code": code,
				"msg":  "success",
			})
		},
		Authenticator: func(ctx context.Context, c *app.RequestContext) (interface{}, error) {
			var req user.LoginReq
			err = c.BindAndValidate(&req)
			if err != nil {
				c.JSON(consts.StatusBadRequest, handler.BadResp(err.Error()))
				return nil, err
			}

			//判断用户是否存在
			existUser := new(model.User)

			if existUser, err = query.User.WithContext(ctx).Where(query.User.Username.Eq(req.Username)).First(); err != nil {

				c.JSON(consts.StatusInternalServerError, handler.FailResp("该用户不存在"))
				return nil, err
			}

			err = bcrypt.CompareHashAndPassword([]byte(existUser.Password), []byte(req.Password))
			if err != nil {
				c.JSON(consts.StatusInternalServerError, handler.FailResp("密码错误"))
				return nil, err
			}
			return existUser, err
		},
		IdentityKey: IdentityKey,
		IdentityHandler: func(ctx context.Context, c *app.RequestContext) interface{} {
			claims := jwt.ExtractClaims(ctx, c)
			return &model.User{
				Username: claims[IdentityKey].(string),
				ID:       claims[identityUserID].(int64),
			}
		},
		PayloadFunc: func(data interface{}) jwt.MapClaims {
			if v, ok := data.(*model.User); ok {
				return jwt.MapClaims{
					IdentityKey:    v.Username,
					identityUserID: v.ID,
				}
			}
			return jwt.MapClaims{}
		},
		HTTPStatusMessageFunc: func(e error, ctx context.Context, c *app.RequestContext) string {
			hlog.CtxErrorf(ctx, "jwt biz err = %+v", e.Error())
			return e.Error()
		},
		Unauthorized: func(ctx context.Context, c *app.RequestContext, code int, message string) {
			c.JSON(http.StatusOK, utils.H{
				"code": code,
				"msg":  message,
			})
		},
	})
	if err != nil {
		panic(err)
	}
}
